package com.xyy.saas.payment.service.job.impl;

import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.*;
import com.xyy.saas.payment.common.constants.AmountConstants;
import com.xyy.saas.payment.common.constants.BaseConstants;
import com.xyy.saas.payment.common.constants.DigitalConstants;
import com.xyy.saas.payment.cores.bo.QueryWithdrawalFeeBo;
import com.xyy.saas.payment.cores.enums.BusinessTypeEnum;
import com.xyy.saas.payment.dao.mapper.RefundOrderMapper;
import com.xyy.saas.payment.dao.mapper.WithdrawOrderMapper;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.pojo.vo.FBankPayRecordsVo;
import com.xyy.saas.payment.service.*;
import com.xyy.saas.payment.service.job.FBankSFTPManage;
import com.xyy.saas.payment.util.AmountCastUtil;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.*;

/**
 * https://blog.csdn.net/aoutlaw/article/details/103559886
 * 1、退款
 * 2、提现
 */
@Slf4j
@Component
public class ChannelSftpService {
    @Resource
    private FbankPayRecordsService fbankPayRecordsService;
    @Resource
    private PayResultOrderService payResultOrderService;
    @Resource
    private RefundOrderMapper refundOrderMapper;
    @Resource
    private PayAccountService payAccountService;
    @Resource
    private WithdrawOrderMapper withdrawOrderMapper;
    @Resource
    private SeparateOrderService separateOrderService;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    /**
     * 上传文件
     * @param merchantNo : key
     * @param localFile  : 本地文件 (完整路径)
     * @param remotePath : SFTP路径 (dir + remotePath + fileName)
     * @param fileName   : 文件名
     * @throws Exception
     */
    public void upload(String logPrefix, String merchantNo, String localFile, String remotePath, String fileName) throws Exception {
        String oriRemotePath = remotePath;
        // 文件上传sftp的文件路径
        if (StringUtils.isBlank(remotePath)) {
            remotePath = FBankSFTPManage.dir(merchantNo, FBankSFTPManage.Type.PINGAN_BILL);
        } else {
            remotePath = FBankSFTPManage.dir(merchantNo, FBankSFTPManage.Type.PINGAN_BILL) + remotePath;
        }
        // 文件上传sftp的完整路径
        String remoteFile = remotePath + "/" + fileName;
        log.info("上传文件到SFTP：{} - {} - {}", localFile, remoteFile, fileName);
        InputStream inputStream = null;
        ChannelSftp sftpChannel = null;
        Session session = null;
        try {
            session = getSession(merchantNo);
            sftpChannel = getChannel(session);
            // 创建目录
            createDir(sftpChannel, remotePath);
            // 进入目录
            sftpChannel.cd(remotePath);
            // 上传文件
            inputStream = new FileInputStream(localFile);
            sftpChannel.put(inputStream, remoteFile);
        } catch (Exception e) {
            deleteFile(logPrefix, merchantNo, oriRemotePath, fileName);
            log.error("{} 上传文件失败 : {}", e);
            throw e;
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
            close(sftpChannel, session);
        }
    }

    public void createDir(ChannelSftp sftpChannel, String remotePath) throws SftpException {
        try {
            if (null == sftpChannel.stat(remotePath)) {
                sftpChannel.mkdir(remotePath);
            }
        } catch (Exception e) {
            try {
                sftpChannel.mkdir(remotePath);
            } catch (Exception ex){
                log.error("创建SFTP文件夹异常");
            }
        }
    }

    /**
     * 删除文件
     * @param merchantNo : key
     * @param remotePath : SFTP路径 (dir + remotePath + fileName)
     * @param fileName   : 文件名
     * @throws Exception
     */
    public void deleteFile(String logPrefix, String merchantNo, String remotePath, String fileName) throws Exception {
        String remoteFile;
        if (StringUtils.isBlank(remotePath)) {
            remoteFile = FBankSFTPManage.dir(merchantNo, FBankSFTPManage.Type.PINGAN_BILL) + File.separator + fileName;
        } else {
            remoteFile = FBankSFTPManage.dir(merchantNo, FBankSFTPManage.Type.PINGAN_BILL) + File.separator + remotePath + File.separator + fileName;
        }
        log.info("{} 从SFTP删除文件：{} {}", logPrefix, remoteFile, fileName);
        ChannelSftp sftpChannel = null;
        Session session = null;
        try {
            session = getSession(merchantNo);
            sftpChannel = getChannel(session);
            try {
                sftpChannel.rm(remoteFile);
            } catch (Exception e){}
        } finally {
            close(sftpChannel, session);
        }
    }

    /**
     * 下载文件
     *
     * @param path     SFTP服务器的文件路径
     * @param file     SFTP服务器上的文件名
     * @return         输入流
     */
    public void download(String merchantNo, String path, String file) throws PaymentException {
        log.info("ChannelSftpService#download path={}, file={}", path, file);
        Session session = getSession(merchantNo);
        ChannelSftp sftp = getChannel(session);

        try {
            if(!StringUtils.isBlank(path)) {
                String realPath = FBankSFTPManage.dir(merchantNo, FBankSFTPManage.Type.QIFUTONG) + path;
                log.info("ChannelSftpService#download realPath={} ", realPath);
                sftp.cd(realPath);
            }

            read(sftp.get(file));
        }
        catch(SftpException e) {
            log.error("文件下载异常，ERROR：" + e.getMessage(), e);
            throw new PaymentException("文件下载异常");
        }
        finally {
            close(sftp, session);
        }
    }

    /**
     * 读取数据
     * @param in
     */
    private void read(InputStream in) {
        BufferedReader br = null;

        try {
            if(in != null) {
                br = new BufferedReader(new InputStreamReader(in));
                String data = null;

                while((data = br.readLine()) != null) {
                    if(Boolean.parseBoolean(ApolloUtil.getProperty("fban_pay_records_download_debug", "false"))) {
                        log.info(data);
                    }

                    FbankPayRecords fbankPayRecords = FBankPayRecordsVo.parse(data);

                    if(fbankPayRecords == null) {
                        log.error("ChannelSftpService#download data parse error, data={}", data);
                        continue;
                    }

                    try {
                        // 校验交易记录是否存在
                        if(fbankPayRecordsService.queryByOrderNo(fbankPayRecords.getOrderNo()) != null) {
                            continue;
                        }

                        fillParams(fbankPayRecords);
                        fbankPayRecordsService.insertSelective(fbankPayRecords);
                    }
                    catch(Exception ex) {
                        log.error("存储数据失败，fbankPayRecords=" + JSONUtils.toJSON(fbankPayRecords) + ", errorMsg=" + ex.getMessage(), ex);
                    }
                }
            }
        }
        catch(Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        finally {
            if(br != null) {
                try {
                    br.close();
                }
                catch(Exception ex) {
                    log.error(ex.getMessage(), ex);
                }
            }
        }
    }

    /**
     * 填充参数
     * 充值（1）
     * 用户提现（2）
     * 退款（38）
     * @param fbankPayRecords
     */
    private void fillParams(FbankPayRecords fbankPayRecords) throws PaymentException{
        // 用户提现
        if(FBankPayRecordsVo.WITHDRAWAL.equals(fbankPayRecords.getTradeType())) {
            WithdrawOrder withdrawOrder = withdrawOrderMapper.selectByChannelWithdrawNo(fbankPayRecords.getOrderNo());

            if(withdrawOrder == null) {
                log.error("ChannelSftpService#download Business Order not fount error, data={}", JSONObject.toJSONString(fbankPayRecords));
                return;
            }

            fbankPayRecords.setBusinessType(withdrawOrder.getBusinessType());

            PayAccount payAccount = payAccountService.getPayAccountByAccountId(withdrawOrder.getUserId());
            /**
             * 佣金的business user id he business order type 一样，导致管理端筛选出错
             */
            fbankPayRecords.setBusinessOrderType("yikuaiqian_fm".equals(payAccount.getBusinessUserId()) ? "yikuaiqian_fm_yjtx" : payAccount.getBusinessUserId());
            fbankPayRecords.setFeeAmount(AmountCastUtil.castFen2Yuan(getFeeByWithdrawalAmount(StringUtils.EMPTY, withdrawOrder.getUserId(), withdrawOrder.getAmount()).getFee()));
            fbankPayRecords.setRemarks("富民提现");
        }
        // 退款
        else if(FBankPayRecordsVo.REFUND.equals(fbankPayRecords.getTradeType())) {
            RefundOrder refundOrder = refundOrderMapper.selectByChannelRefundNo(fbankPayRecords.getOrderNo(), fbankPayRecords.getOrderNo());

            if(refundOrder == null) {
                log.error("ChannelSftpService#download Business Order not fount error, data={}", JSONObject.toJSONString(fbankPayRecords));
                return;
            }

            PayResultOrder payResultOrder = payResultOrderService.selectByChannelChargeNo(refundOrder.getChannelChargeNo());
            fbankPayRecords.setBusinessType(payResultOrder.getBusinessType());
            fbankPayRecords.setBusinessOrderType(payResultOrder.getBusinessOrderType());

            String fee = AmountCastUtil.roundByFBankRefund(payResultOrder.getAmount(), refundOrder.getAmount());
            fbankPayRecords.setFeeAmount(DigitalConstants.ZERO_000.equals(fee) ? fee : "-" + fee);
            fbankPayRecords.setExtra(refundOrder.getBusinessRefundNo());
            fbankPayRecords.setRemarks("富民退款");
        }
        else if(FBankPayRecordsVo.RECHARGE.equals(fbankPayRecords.getTradeType())) {
            fbankPayRecords.setRemarks("富民充值");
            fbankPayRecords.setFeeAmount(fbankPayRecords.getAmount());
        }
        else if(FBankPayRecordsVo.ACCOUNT_TRANSFER.equals(fbankPayRecords.getTradeType())) {
            SeparateOrder separateOrder = separateOrderService.querySeparateOrderBySeparateNo(fbankPayRecords.getClientSerialNo());

            if(separateOrder != null) {
                PayAccount payAccount = payAccountService.getPayAccountByAccountId(separateOrder.getCustMerchant());
                fbankPayRecords.setBusinessType(payAccount.getBusinessIdType());
                fbankPayRecords.setBusinessOrderType(payAccount.getBusinessUserId());
            }

            fbankPayRecords.setRemarks("富民账户转账");
        }
        else {
            throw new UnsupportedOperationException("Unsupported type:" + fbankPayRecords.getTradeType());
        }
    }

    /**
     * 连接SFTP服务器
     */
    private Session getSession(String merchantNo) throws PaymentException {
        JSch jsch = new JSch();

        try {
            //采用指定的端口连接服务器
            Session session = jsch.getSession(FBankSFTPManage.username(merchantNo), FBankSFTPManage.host(merchantNo), FBankSFTPManage.port(merchantNo));
            session.setPassword(FBankSFTPManage.password(merchantNo));
            //优先使用 password 验证   注：session.connect()性能低，使用password验证可跳过gssapi认证，提升连接服务器速度
            session.setConfig("PreferredAuthentications","password");
            //设置第一次登陆的时候提示，可选值：(ask | yes | no)
            session.setConfig("StrictHostKeyChecking","no");
            session.connect();
            return session;
        }
        catch(JSchException e){
            log.error("SFTP服务器连接异常！！" + e.getMessage(), e);
        }

        throw new PaymentException("SFTP服务器连接异常");
    }

    /**
     * 创建sftp通信通道
     * @param session
     * @return
     */
    private ChannelSftp getChannel(Session session)  throws PaymentException {
        try {
            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftp = (ChannelSftp) channel;
            log.info("sftp server connect success !!");
            return sftp;
        }
        catch(JSchException e) {
            log.error("SFTP服务器连接异常！！" + e.getMessage(), e);
        }

        throw new PaymentException("SFTP服务器连接异常");
    }

    /**
     * 关闭SFTP连接
     */
    private void close(ChannelSftp sftp, Session session) {
        if(sftp != null) {
            if(sftp.isConnected()) {
                sftp.disconnect();
                log.info("sftp is close already");
            }
        }

        if(session != null) {
            if(session.isConnected()) {
                session.disconnect();
                log.info("session is close already");
            }
        }
    }

    public QueryWithdrawalFeeBo getFeeByWithdrawalAmount(String businessType, String userId, int amount) throws PaymentException {
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(userId);

        if(thirdUserInfo == null || thirdUserInfo.getThirdUserId().isEmpty()) {
            throw new PaymentException(ResultCodeEnum.GET_USER_STATE_ERROR);
        }

        QueryWithdrawalFeeBo queryWithdrawalFeeBo = new QueryWithdrawalFeeBo();

        if(BusinessTypeEnum.EC.getType().equals(businessType)) {
            queryWithdrawalFeeBo.setFee(0);
            queryWithdrawalFeeBo.setExtraMsg("药帮忙手续费平台承担 0元/笔");
            return queryWithdrawalFeeBo;
        }

        if(BaseConstants.FUMIN_BANK_NAME.equals(thirdUserInfo.getSettleBank())) {
            queryWithdrawalFeeBo.setFee(0);
            queryWithdrawalFeeBo.setExtraMsg("富民银行卡提现 0元/笔");
            return queryWithdrawalFeeBo;
        }

        if(BaseConstants.PERSONAL.equals(thirdUserInfo.getUserType())) {
            queryWithdrawalFeeBo.setFee(20);
            queryWithdrawalFeeBo.setExtraMsg("个人银行卡提现 0.2元/笔");
            return queryWithdrawalFeeBo;
        }

        if(BaseConstants.MERCHANT.equals(thirdUserInfo.getUserType())) {
            //5万及以下 5毛 （含）
            if(amount <= AmountConstants.WITHDRAWAL_AMOUNT_5W) {
                queryWithdrawalFeeBo.setFee(50);
                queryWithdrawalFeeBo.setExtraMsg(AmountConstants.withdrawalExtraMsg);
                return queryWithdrawalFeeBo;
            }

            //20万以下 （不含）
            if(amount < AmountConstants.WITHDRAWAL_AMOUNT_20W) {
                queryWithdrawalFeeBo.setFee(500);
                queryWithdrawalFeeBo.setExtraMsg(AmountConstants.withdrawalExtraMsg);
                return queryWithdrawalFeeBo;
            }
            else {
                queryWithdrawalFeeBo.setFee(600);
                queryWithdrawalFeeBo.setExtraMsg(AmountConstants.withdrawalExtraMsg);
                return queryWithdrawalFeeBo;
            }
        }

        throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
    }
}